package cnsukidayo.com.gitee.handler.filter;

import cnsukidayo.com.gitee.exception.AuthenticationException;
import cnsukidayo.com.gitee.handler.handler.AuthenticationFailureHandler;
import cnsukidayo.com.gitee.handler.handler.DefaultAuthenticationFailureHandler;
import cnsukidayo.com.gitee.service.PetShelterService;
import cnsukidayo.com.gitee.service.UserService;
import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

/**
 * Api authentication Filter
 *
 * @author johnniang
 */
@Component
@Order(0)
public class ApiAuthenticationFilter extends OncePerRequestFilter {

    private Set<String> urlPatterns = new LinkedHashSet<>();
    private Set<String> excludeUrlPatterns = new HashSet<>(16);
    protected final AntPathMatcher antPathMatcher;
    private final UrlPathHelper urlPathHelper = new UrlPathHelper();
    private AuthenticationFailureHandler failureHandler;
    private final UserService userService;
    private final PetShelterService petStoreService;

    public ApiAuthenticationFilter(UserService userService,
                                   PetShelterService petStoreService) {
        this.userService = userService;
        this.petStoreService = petStoreService;
        this.antPathMatcher = new AntPathMatcher();
        urlPatterns.add("/user/**");
        excludeUrlPatterns.add("/user/user/login");
        excludeUrlPatterns.add("/user/petshelter/login");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 判断当前用户是否登陆,如果没有登陆则打回请求
        String token = "";
        if (request.getCookies() == null) {
            getFailureHandler().onFailure(request, response, new AuthenticationException("未登录，请登录后访问"));
            return;
        }
        for (Cookie cookie : request.getCookies()) {
            if (cookie.getName().equals("token")) {
                token = cookie.getValue();
            }
        }
        if (token.isEmpty()) {
            getFailureHandler().onFailure(request, response, new AuthenticationException("未登录，请登录后访问"));
            return;
        }
        Optional<Integer> userID = userService.queryUserIDByToken(token);
        Optional<Integer> petStoreID = petStoreService.queryPetShelterIDByToken(token);
        if (userID.isEmpty() && petStoreID.isEmpty()) {
            getFailureHandler().onFailure(request, response, new AuthenticationException("token过期或已不存在!"));
            return;
        }
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        Assert.notNull(request, "Http servlet request must not be null");
        boolean result = excludeUrlPatterns.stream()
                .anyMatch(p -> antPathMatcher.match(p, urlPathHelper.getRequestUri(request)));

        return result || urlPatterns.stream()
                .noneMatch(p -> antPathMatcher.match(p, urlPathHelper.getRequestUri(request)));

    }

    @NonNull
    private AuthenticationFailureHandler getFailureHandler() {
        if (failureHandler == null) {
            synchronized (this) {
                if (failureHandler == null) {
                    // Create default authentication failure handler
                    DefaultAuthenticationFailureHandler failureHandler =
                            new DefaultAuthenticationFailureHandler();

                    this.failureHandler = failureHandler;
                }
            }
        }
        return failureHandler;
    }


}
